package org.unidata.mdm.meta.serialization;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import org.unidata.mdm.meta.exception.MetaExceptionIds;
import org.unidata.mdm.meta.exception.ModelRuntimeException;
import org.unidata.mdm.meta.type.model.DataModel;
import org.unidata.mdm.meta.type.model.enumeration.EnumerationsModel;
import org.unidata.mdm.meta.type.model.measurement.MeasurementUnitsModel;
import org.unidata.mdm.meta.type.model.sourcesystem.SourceSystemsModel;
import org.unidata.mdm.system.serialization.xml.XmlObjectSerializer;
import org.unidata.mdm.system.util.CompressUtils;

/**
 * @author Mikhail Mikhailov on Oct 14, 2020
 */
public final class MetaSerializer {
    /**
     * Data model zip entry name.
     */
    public static final String DATA_MODEL_ZIP_ENTRY = "data-model";
    /**
     * Enumerations model zip entry name.
     */
    public static final String ENUMERATIONS_MODEL_ZIP_ENTRY = "enumerations-model";
    /**
     * SSs model zip entry name.
     */
    public static final String SOURCE_SYSTEMS_MODEL_ZIP_ENTRY = "source-systems-model";
    /**
     * SSs model zip entry name.
     */
    public static final String MEASUREMENT_UNITS_MODEL_ZIP_ENTRY = "measurement-units-model";
    /**
     * Constructor.
     */
    private MetaSerializer() {
        super();
    }
    /**
     * Does uncompress default ZIP compressed data model XML representation.
     * @param data the compressed data
     * @return model
     */
    public static DataModel modelFromCompressedXml(byte[] data) {

        byte[] uncompressed;
        try {
            uncompressed = CompressUtils.unzip(data, DATA_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new ModelRuntimeException("Cannot uncompress data model.", e,
                    MetaExceptionIds.EX_META_CANNOT_UNCOMPRESS_DATA);
        }

        return XmlObjectSerializer
                .getInstance()
                .fromXmlString(DataModel.class, new String(uncompressed, StandardCharsets.UTF_8));
    }
    /**
     * Does compress data model to default ZIP compressed XML representation.
     * @param model the model
     * @return bytes
     */
    public static byte[] modelToCompressedXml(DataModel model) {

        String content = XmlObjectSerializer.getInstance().toXmlString(model, false);
        byte[] compressed;
        try {
            compressed = CompressUtils.zip(content.getBytes(StandardCharsets.UTF_8), DATA_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new ModelRuntimeException("Cannot compress data model.", e,
                    MetaExceptionIds.EX_META_CANNOT_COMPRESS_DATA);
        }

        return compressed;
    }
    /**
     * Does uncompress default ZIP compressed enumerations model XML representation.
     * @param data the compressed data
     * @return model
     */
    public static EnumerationsModel enumerationsFromCompressedXml(byte[] data) {

        byte[] uncompressed;
        try {
            uncompressed = CompressUtils.unzip(data, ENUMERATIONS_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new ModelRuntimeException("Cannot uncompress enumerations model.", e,
                    MetaExceptionIds.EX_META_CANNOT_UNCOMPRESS_ENUMERATIONS);
        }

        return XmlObjectSerializer.getInstance()
            .fromXmlString(EnumerationsModel.class, new String(uncompressed, StandardCharsets.UTF_8));
    }
    /**
     * Does compress enumerations model to default ZIP compressed XML representation.
     * @param model the model
     * @return bytes
     */
    public static byte[] enumerationsToCompressedXml(EnumerationsModel model) {

        String content = XmlObjectSerializer.getInstance().toXmlString(model, false);
        byte[] compressed;
        try {
            compressed = CompressUtils.zip(content.getBytes(StandardCharsets.UTF_8), ENUMERATIONS_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new ModelRuntimeException("Cannot compress enumerations model.", e,
                    MetaExceptionIds.EX_META_CANNOT_COMPRESS_ENUMERATIONS);
        }

        return compressed;
    }
    /**
     * Does uncompress default ZIP compressed source systems model XML representation.
     * @param data the compressed data
     * @return model
     */
    public static SourceSystemsModel sourceSystemsFromCompressedXml(byte[] data) {

        byte[] uncompressed;
        try {
            uncompressed = CompressUtils.unzip(data, SOURCE_SYSTEMS_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new ModelRuntimeException("Cannot uncompress source systems model.",
                    e, MetaExceptionIds.EX_META_CANNOT_UNCOMPRESS_SOURCE_SYSTEMS);
        }

        return XmlObjectSerializer.getInstance()
            .fromXmlString(SourceSystemsModel.class, new String(uncompressed, StandardCharsets.UTF_8));
    }
    /**
     * Does compress source systems model to default ZIP compressed XML representation.
     * @param model the model
     * @return bytes
     */
    public static byte[] sourceSystemsToCompressedXml(SourceSystemsModel model) {

        String content = XmlObjectSerializer.getInstance().toXmlString(model, false);
        byte[] compressed;
        try {
            compressed = CompressUtils.zip(content.getBytes(StandardCharsets.UTF_8), SOURCE_SYSTEMS_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new ModelRuntimeException("Cannot compress source systems model.", e,
                    MetaExceptionIds.EX_META_CANNOT_COMPRESS_SOURCE_SYSTEMS);
        }

        return compressed;
    }
    /**
     * Does uncompress default ZIP compressed measurement units model XML representation.
     * @param data the compressed data
     * @return model
     */
    public static MeasurementUnitsModel measurementUnitsFromCompressedXml(byte[] data) {

        byte[] uncompressed;
        try {
            uncompressed = CompressUtils.unzip(data, MEASUREMENT_UNITS_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new ModelRuntimeException("Cannot uncompress measurement units model.", e,
                    MetaExceptionIds.EX_META_CANNOT_UNCOMPRESS_MEASUREMENT_UNITS);
        }

        return XmlObjectSerializer.getInstance()
            .fromXmlString(MeasurementUnitsModel.class, new String(uncompressed, StandardCharsets.UTF_8));
    }
    /**
     * Does compress measurement units model to default ZIP compressed XML representation.
     * @param model the model
     * @return bytes
     */
    public static byte[] measurementUnitsToCompressedXml(MeasurementUnitsModel model) {

        String content = XmlObjectSerializer.getInstance().toXmlString(model, false);
        byte[] compressed;
        try {
            compressed = CompressUtils.zip(content.getBytes(StandardCharsets.UTF_8), MEASUREMENT_UNITS_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new ModelRuntimeException("Cannot compress measurement units model.", e,
                    MetaExceptionIds.EX_META_CANNOT_COMPRESS_MEASUREMENT_UNITS);
        }

        return compressed;
    }
}
